#include "geocaching.h"

Geocaching::Geocaching(void):
    lastPointID(1), lastRouteID(1), lastTrackID(1), lastMapID(1),\
    currentRouteID(0), currentMapID(0), currentTrackID(0), mainReceiver(new GPS),\
    mainPointList(), mainRouteList(), mainTrackList(), mainMapList(),\
    manualTrack(new Track), autoTrack(new Track),\
    manualTrackStatus(false), autoTrackStatus(false), autoTrackThread(autoTrack, mainReceiver)
{
    connect(mainReceiver, SIGNAL(CoordinatesChanged()), this, SIGNAL(CoordinatesChanged()));
    LoadData();
}

Geocaching::~Geocaching()
{
    SaveData();
    if(autoTrackStatus)     StopAutoTrackRecord();
    if(manualTrackStatus)   StopManualTrackRecord();
    if(mainReceiver)    delete mainReceiver;
    if(manualTrack)     delete manualTrack;
    if(autoTrack)       delete autoTrack;
    autoTrackThread.stop();
    autoTrackThread.wait();
    mainPointList.clear();
    mainRouteList.clear();
    mainTrackList.clear();
    mainMapList.clear();
}

void Geocaching::LoadData(void)
{
    GpxManager gpx;
    gpx.read(mainPointList,mainMapList);
}

void Geocaching::SaveData(void) const
{
    GpxManager gpx;
    gpx.write(mainPointList,mainRouteList,mainTrackList,mainMapList);
}

unsigned long Geocaching::AddPoint(Point *aPoint)
{
    if(aPoint)
    {
        aPoint->SetID(lastPointID++);
        mainPointList.push_back(*aPoint);
        emit PointAdded(*aPoint);
        SaveData();
        return lastPointID;
    }
    else
        return 0;
}

bool Geocaching::EditPoint(const unsigned long& aID, Point *aPoint)
{
    if(aPoint)
    {
        unsigned long i = 0, N = mainPointList.size();
        for(i = 0; i < N; ++i)
            if(mainPointList[i].GetID() == aID)
            {
                unsigned long j = 0, M = aPoint->GetFileCount();
                mainPointList[i].ClearPoint();
                mainPointList[i].SetName(aPoint->GetName());
                mainPointList[i].SetCoordinates(aPoint->GetCoordinates());
                for(j = 0; j < M; ++j)
                    mainPointList[i].AddFile(aPoint->GetFileName(j));
                emit PointEdited(*aPoint);
                Route currRoute;
                if(GetCurrentRoute(&currRoute))
                    if(currRoute.IsPointBelong(aID))
                        emit CurrentRouteChanged();
                SaveData();
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::GetPoint(const unsigned long& aID, Point *aPoint) const
{
    if(aPoint)
    {
        aPoint->ClearPoint();
        unsigned long i = 0, N = mainPointList.size();
        for(i = 0; i < N; ++i)
            if(mainPointList[i].GetID() == aID)
            {
                unsigned long j = 0, M = mainPointList[i].GetFileCount();
                aPoint->SetID(mainPointList[i].GetID());
                aPoint->SetName(mainPointList[i].GetName());
                aPoint->SetCoordinates(mainPointList[i].GetCoordinates());
                for(j = 0; j < M; ++j)
                    aPoint->AddFile(mainPointList[i].GetFileName(j));
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::DeletePoint(const unsigned long& aID)
{
    unsigned long i = 0, N = mainPointList.size();
    for(i = 0; i < N; ++i)
        if(mainPointList[i].GetID() == aID)
        {
            mainPointList.removeAt(i);
            emit PointDeleted(aID);
            if(currentRouteID)
                foreach(Route item, mainRouteList)
                    if(item.RemovePoint(aID) && item.GetID() == currentRouteID)
                        emit CurrentRouteChanged();
            SaveData();
            return true;
        }
    return false;
}

bool Geocaching::AddRoute(Route* aRoute, const bool &aSetCurrent)
{
    if(aRoute)
    {
        aRoute->SetID(lastRouteID++);
        mainRouteList.push_back(*aRoute);
        emit RouteAdded(*aRoute);
        if(aSetCurrent)
            SetCurrentRoute(aRoute->GetID());
        SaveData();
        return true;
    }
    else
        return false;
}

bool Geocaching::EditRoute(const unsigned long& aID, Route* aRoute, const bool &aSetCurrent)
{
    if(aRoute)
    {
        unsigned long i = 0, N = mainRouteList.size();
        for(i = 0; i < N; ++i)
            if(mainRouteList[i].GetID() == aID)
            {
                unsigned long j = 0, M = aRoute->GetPointCount();
                mainRouteList[i].ClearRoute();
                mainRouteList[i].SetName(aRoute->GetName());
                for(j = 0; j < M; ++j)
                    mainRouteList[i].AddPoint(aRoute->GetPoint(j));
                M = aRoute->GetFileCount();
                for(j = 0; j < M; ++j)
                    mainRouteList[i].AddFile(aRoute->GetFileName(j));
                emit RouteEdited(*aRoute);
                if(aID == currentRouteID)
                    emit CurrentRouteChanged();
                if(aSetCurrent)
                    SetCurrentRoute(aRoute->GetID());
                SaveData();
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::GetRoute(const unsigned long& aID, Route* aRoute) const
{
    if(aRoute)
    {
        aRoute->ClearRoute();
        unsigned long i = 0, N = mainRouteList.size();
        for(i = 0; i < N; ++i)
            if(mainRouteList[i].GetID() == aID)
            {
                unsigned long j = 0, M = mainRouteList[i].GetPointCount();
                aRoute->SetID(mainRouteList[i].GetID());
                aRoute->SetName(mainRouteList[i].GetName());
                for(j = 0; j < M; ++j)
                    aRoute->AddPoint(mainRouteList[i].GetPoint(j));
                M = mainRouteList[i].GetFileCount();
                for(j = 0; j < M; ++j)
                    aRoute->AddFile(mainRouteList[i].GetFileName(j));
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::DeleteRoute(const unsigned long& aID)
{
    unsigned long i = 0, N = mainRouteList.size();
    for(i = 0; i < N; ++i)
        if(mainRouteList[i].GetID() == aID)
        {
            mainRouteList.removeAt(i);
            emit RouteDeleted(aID);
            if(aID == currentRouteID)
                emit CurrentRouteChanged();
            SaveData();
            return true;
        }
    return false;
}

bool Geocaching::SetCurrentRoute(const unsigned long &aID)
{
    if(aID)
    {
        unsigned long i = 0, N = mainRouteList.size();
        for(i = 0; i < N; ++i)
            if(mainRouteList[i].GetID() == aID)
            {
                currentRouteID = aID;
                emit CurrentRouteChanged();
                return true;
            }
        return false;
    }
    else
    {
        unsigned long tmpID = currentRouteID;
        currentRouteID = 0;
        if(tmpID)
            emit CurrentRouteChanged();
        return true;
    }
}

unsigned long Geocaching::GetCurrentRouteID(void) const
{
    return currentRouteID;
}

bool Geocaching::GetCurrentRoute(Route *aRoute) const
{
    if(aRoute && currentRouteID)
    {
        aRoute->ClearRoute();
        unsigned long i = 0, N = mainRouteList.size();
        for(i = 0; i < N; ++i)
            if(mainRouteList[i].GetID() == currentRouteID)
            {
                unsigned long j = 0, M = mainRouteList[i].GetPointCount();
                aRoute->SetID(mainRouteList[i].GetID());
                aRoute->SetName(mainRouteList[i].GetName());
                for(j = 0; j < M; ++j)
                    aRoute->AddPoint(mainRouteList[i].GetPoint(j));
                M = mainRouteList[i].GetFileCount();
                for(j = 0; j < M; ++j)
                    aRoute->AddFile(mainRouteList[i].GetFileName(j));
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::AddMap(Map *aMap, const bool &aSetCurrent)
{
    if(aMap)
    {
        aMap->SetID(lastMapID++);
        mainMapList.push_back(*aMap);
        emit MapAdded(*aMap);
        if(aSetCurrent)
            SetCurrentMap(aMap->GetID());
        SaveData();
        return true;
    }
    else
        return false;
}

bool Geocaching::EditMap(const unsigned long &aID, Map *aMap, const bool &aSetCurrent)
{
    if(aMap)
    {
        unsigned long i = 0, N = mainMapList.size();
        for(i = 0; i < N; ++i)
            if(mainMapList[i].GetID() == aID)
            {
                GeoCoordinates topLeft, topRight, bottomRight, bottomLeft;
                aMap->GetCalibration(topLeft, topRight, bottomRight, bottomLeft);
                mainMapList[i].ClearMap();
                mainMapList[i].SetName(aMap->GetName());
                mainMapList[i].SetFilePath(aMap->GetFilePath());
                mainMapList[i].SetCalibration(topLeft, topRight, bottomRight, bottomLeft);
                emit MapEdited(*aMap);
                if(aSetCurrent)
                    SetCurrentMap(aID);
                SaveData();
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::GetMap(const unsigned long &aID, Map *aMap) const
{
    if(aMap)
    {
        aMap->ClearMap();
        unsigned long i = 0, N = mainMapList.size();
        for(i = 0; i < N; ++i)
            if(mainMapList[i].GetID() == aID)
            {
                GeoCoordinates topLeft, topRight, bottomRight, bottomLeft;
                mainMapList[i].GetCalibration(topLeft, topRight, bottomRight, bottomLeft);
                aMap->SetID(mainMapList[i].GetID());
                aMap->SetName(mainMapList[i].GetName());
                aMap->SetFilePath(mainMapList[i].GetFilePath());
                aMap->SetCalibration(topLeft, topRight, bottomRight, bottomLeft);
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::DeleteMap(const unsigned long &aID)
{
    unsigned long i = 0, N = mainMapList.size();
    for(i = 0; i < N; ++i)
        if(mainMapList[i].GetID() == aID)
        {
            mainMapList.removeAt(i);
            emit MapDeleted(aID);
            if(aID == currentMapID)
                SetCurrentMap(0);
            SaveData();
            return true;
        }
    return false;
}

unsigned long Geocaching::GetCurrentMapID(void) const
{
    return currentMapID;
}

bool Geocaching::GetCurrentMap(Map *aMap) const
{
    if(aMap && currentMapID)
    {
        aMap->ClearMap();
        unsigned long i = 0, N = mainMapList.size();
        for(i = 0; i < N; ++i)
            if(mainMapList[i].GetID() == currentMapID)
            {
                GeoCoordinates topLeft, topRight, bottomRight, bottomLeft;
                mainMapList[i].GetCalibration(topLeft, topRight, bottomRight, bottomLeft);
                aMap->SetID(mainMapList[i].GetID());
                aMap->SetName(mainMapList[i].GetName());
                aMap->SetFilePath(mainMapList[i].GetFilePath());
                aMap->SetCalibration(topLeft, topRight, bottomRight, bottomLeft);
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::SetCurrentMap(const unsigned long &aID)
{
    if(aID)
    {
        unsigned long i = 0, N = mainMapList.size();
        for(i = 0; i < N; ++i)
            if(mainMapList[i].GetID() == aID)
            {
                currentMapID = aID;
                emit CurrentMapChanged();
                return true;
            }
        return false;
    }
    else
    {
        unsigned long tmpID = currentMapID;
        currentMapID = 0;
        if(tmpID)
            emit CurrentMapChanged();
        return true;
    }
}

bool Geocaching::GetTrack(const unsigned long &aID, Track *aTrack) const
{
    if(aTrack)
    {
        aTrack->ClearTrack();
        foreach(Track item, mainTrackList)
            if(item.GetID() == aID)
            {
                unsigned long j = 0, M = item.GetPointCount();
                TrackPoint tmpTrackPoint;
                aTrack->SetID(item.GetID());
                aTrack->SetName(item.GetName());
                for(j = 0; j < M; ++j)
                {
                    item.GetPoint(&tmpTrackPoint, j);
                    aTrack->AddPoint(tmpTrackPoint);
                }
                return true;
            }
        return false;
    }
    else
        return false;
}

bool Geocaching::DeleteTrack(const unsigned long &aID)
{
    unsigned long i = 0, N = mainTrackList.size();
    for(i = 0; i < N; ++i)
        if(mainTrackList[i].GetID() == aID)
        {
            mainTrackList.removeAt(i);
            emit TrackDeleted(aID);
            if(aID == currentTrackID)
                emit CurrentTrackChanged();
            SaveData();
            return true;
        }
    return false;
}

bool Geocaching::SetCurrentTrack(const unsigned long &aID)
{
    if(aID)
    {
        foreach(Track item, mainTrackList)
            if(item.GetID() == aID)
            {
                currentTrackID = aID;
                emit CurrentTrackChanged();
                return true;
            }
        return false;
    }
    else
    {
        unsigned long tmpID = currentTrackID;
        currentTrackID = 0;
        if(tmpID)
            emit CurrentTrackChanged();
        return true;
    }
}

unsigned long Geocaching::GetCurrentTrackID(void) const
{
    return currentTrackID;
}

bool Geocaching::GetCurrentTrack(Track *aTrack) const
{
    if(aTrack && currentTrackID)
    {
        aTrack->ClearTrack();
        foreach(Track item, mainTrackList)
            if(item.GetID() == currentTrackID)
            {
                unsigned long j = 0, M = item.GetPointCount();
                TrackPoint tmpTrackPoint;
                aTrack->SetID(item.GetID());
                aTrack->SetName(item.GetName());
                for(j = 0; j < M; ++j)
                {
                    item.GetPoint(&tmpTrackPoint, j);
                    aTrack->AddPoint(tmpTrackPoint);
                }
                return true;
            }
        return false;
    }
    else
        return false;
}

void Geocaching::StartAutoTrackRecord(void)
{
    autoTrack->ClearTrack();
    autoTrackStatus = true;
    autoTrackThread.start();
}

unsigned long Geocaching::StopAutoTrackRecord(const QString &aName, const bool &aSave, const bool &aSetCurrent)
{
    autoTrackStatus = false;
    autoTrackThread.stop();
    if(aSave && (autoTrack->GetPointCount() > 0))
    {
        autoTrack->SetName(aName);
        autoTrack->SetID(lastTrackID++);
        mainTrackList.push_back(*autoTrack);
        emit TrackAdded(*autoTrack);
        if(aSetCurrent)
            SetCurrentTrack(lastTrackID - 1);
        SaveData();
        return lastTrackID - 1;
    }
    return 0;
}

void Geocaching::StartManualTrackRecord(void)
{
    manualTrack->ClearTrack();
    manualTrackStatus = true;
    AddManualTrackPoint();
}

void Geocaching::AddManualTrackPoint(void)
{
    if(manualTrackStatus)
    {
        GeoCoordinates tmpCoord;
        if(mainReceiver->GetCoordinates(tmpCoord))
            manualTrack->AddPoint(QDateTime::currentDateTime(), tmpCoord);
    }
}

unsigned long Geocaching::StopManualTrackRecord(const QString &aName, const bool &aSave, const bool &aSetCurrent)
{
    manualTrackStatus = false;
    if(aSave && (manualTrack->GetPointCount() > 0))
    {
        manualTrack->SetName(aName);
        manualTrack->SetID(lastTrackID++);
        mainTrackList.push_back(*manualTrack);
        emit TrackAdded(*manualTrack);
        if(aSetCurrent)
            SetCurrentTrack(lastTrackID - 1);
        SaveData();
        return lastTrackID - 1;
    }
    return 0;
}

bool Geocaching::GetManualTrackStatus(void) const
{
    return manualTrackStatus;
}

bool Geocaching::GetAutoTrackStatus(void) const
{
    return autoTrackStatus;
}

QList <Point> Geocaching::GetPointList(void) const
{
    return mainPointList;
}

QList <Route> Geocaching::GetRouteList(void) const
{
    return mainRouteList;
}

QList <Track> Geocaching::GetTrackList(void) const
{
    return mainTrackList;
}

QList <Map> Geocaching::GetMapList(void) const
{
    return mainMapList;
}

ReceiverStatus Geocaching::GetGPSStatus(void) const
{
    return mainReceiver->GetStatus();
}

bool Geocaching::SwitchGPS(const bool &aStatus)
{
    if(aStatus) return mainReceiver->TurnOn();
    else        return mainReceiver->TurnOff();
}

bool Geocaching::SetupGPS(const SerialPortSettings &aSettings, const QString &aName) const
{
    mainReceiver->TurnOff();
    return mainReceiver->Setup(aSettings, aName);
}

bool Geocaching::GPSInfo(nmeaINFO &info)
{
    return mainReceiver->GetInfo(info);
}

bool Geocaching::GetCoordinates(GeoCoordinates &aCoord) const
{
    return mainReceiver->GetCoordinates(aCoord);
}
